package server;

import java.io.IOException;
import java.util.Random;
import java.util.Vector;

import javax.microedition.io.Connector;
import javax.microedition.io.Datagram;

import server.interfaces.IMobileAgentCommunicationChannelReceiver;
import server.interfaces.IMobileAgentCommunicationChannelSender;
import server.interfaces.IMobileAgentExecutionEngine;

import com.Constants;
import com.Event;
import com.LedsManager;
import com.ParamNotFoundException;
import com.ParamsLabel;
import com.sun.spot.io.j2me.radiogram.RadiogramConnection;
import com.sun.spot.resources.transducers.LEDColor;
import com.sun.spot.util.Utils;
import com.sun.squawk.util.StringTokenizer;

public class MobileAgentCommunicationChannelReceiver implements IMobileAgentCommunicationChannelReceiver {
	


	private IMobileAgentExecutionEngine executionEngine = null;
	private IMobileAgentCommunicationChannelSender communicationChannelSender = null;
	private String communicationPort = null;	
	
	/**
	 * Creates a MobileAgentCommunicationChannelReceiver
	 * @param executionEngine IMobileAgentExecutionEngine component
	 * @param communicationChannelSender IMobileAgentCommunicationChannelSender component
	 * @param communicationPort Communication Port number
	 */
	public MobileAgentCommunicationChannelReceiver (IMobileAgentExecutionEngine executionEngine, IMobileAgentCommunicationChannelSender communicationChannelSender, String communicationPort) {
		this.executionEngine = executionEngine;
		this.communicationChannelSender = communicationChannelSender;
		this.communicationPort = communicationPort;
	}
	
	
	public void run() {
		
		RadiogramConnection conn = null;
		try {
			conn = (RadiogramConnection) Connector.open("radiogram://:" + this.communicationPort);
			
			while (true) {
				try {
					
					Datagram dg = conn.newDatagram(conn.getMaximumLength());
					LedsManager.radioStandby();					
					conn.receive(dg);
					LedsManager.radioTransmission(true,true);				
					
					this.parseDatagram(dg);
				}
				catch (IOException ioe) {						
					LedsManager.error(LEDColor.BLUE);
					ioe.printStackTrace();
				}
				
			}
			
		}
		catch (Exception e) {
			LedsManager.error(LEDColor.BLUE);
			e.printStackTrace();
		}
		finally {
			try {
				conn.close();
			}
			catch (IOException e) {
				LedsManager.error(LEDColor.BLUE);
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * This function receives a datagram and dispatches it to the right component. 
	 * @param dg the datagram to dispatch
	 * @throws IOException the datagram could be wrong
	 */
	private void parseDatagram(Datagram dg) throws IOException{
		
		String event = dg.readUTF();
		StringTokenizer eventToken = new StringTokenizer(event, Constants.COMMUNICATION_SEPARATOR);		
		byte evtName = -1;
		if (eventToken.hasMoreTokens()) {			
			evtName = Byte.parseByte(eventToken.nextToken());
			switch(evtName) {
				case Event.MGR_REQUEST:
					
					if (eventToken.hasMoreTokens()) {
						String agentName = eventToken.nextToken();
						String address = dg.getAddress();
						this.executionEngine.getMigrationManger().migrationRequest(address, agentName);
						
					}
					
					break;
				case Event.MGR_ACK:
					if (eventToken.hasMoreTokens()) {
						String agentName = eventToken.nextToken();
						String address = dg.getAddress();		
						this.executionEngine.getMigrationManger().migrationAckReceived(address, agentName);
					}
				case Event.MGR_EXECUTED:
					if (eventToken.hasMoreTokens()) {
						String agentName = eventToken.nextToken();
						this.executionEngine.migrationExecuted(agentName);
					}
					break;
				case Event.DSC_PUBLISH:
					String addressPublish = dg.getAddress();
					new DiscoveryAnswer(this.executionEngine, this.communicationChannelSender, addressPublish).start();
					break;
				case Event.DSC_ANSWER:
				case Event.DSC_REFRESH:
					String address = dg.getAddress();
					Vector agents = new Vector();
					while (eventToken.hasMoreTokens()) {							
						agents.addElement(eventToken.nextToken());
					}
					this.executionEngine.addNeighbor(address, agents);
					break;
				
				case Event.AGN_CREATION:
					if (eventToken.hasMoreTokens()) {
						String stringEvent = eventToken.nextToken();
						Event creationEvent = new Event(stringEvent);
						String paramsString;
						try {
							paramsString = creationEvent.getParam(ParamsLabel.AGT_PARAMS);
							Vector paramsVector = new Vector();
							StringTokenizer paramsTokenizer = new StringTokenizer(paramsString, Event.VECTOR_PARAM_SEPARATOR + "");
							while (paramsTokenizer.hasMoreTokens())
								paramsVector.addElement(paramsTokenizer.nextToken());
							String [] paramsArray = new String [paramsVector.size()];
							paramsVector.copyInto(paramsArray);
							this.executionEngine.create(creationEvent.getParam(ParamsLabel.AGT_SUFFIX), creationEvent.getSourceID(), creationEvent.getParam(ParamsLabel.AGT_NAME), paramsArray, creationEvent.getParam(ParamsLabel.EVT_ADDRESS));
						}
						catch (ParamNotFoundException e) {
							LedsManager.error(LEDColor.YELLOW);
							e.printStackTrace();
						}
					}
					break;
					
				case Event.MSG:
				default:
					if (eventToken.hasMoreTokens()) {
						String stringEvent = eventToken.nextToken();
						Event eventMsg = new Event(stringEvent);
						this.executionEngine.send(eventMsg.getSourceID(), eventMsg.getTargetID(), eventMsg, true);
					}
					break;
			}
		}
	}
	
}

class DiscoveryAnswer extends Thread{
	
	private IMobileAgentExecutionEngine executionEngine;
	private String address;
	private IMobileAgentCommunicationChannelSender communicationChannelSender;
	
	public DiscoveryAnswer(IMobileAgentExecutionEngine executionEngine, IMobileAgentCommunicationChannelSender communicationChannelSender, String address) {
		this.executionEngine = executionEngine;
		this.communicationChannelSender = communicationChannelSender;
		this.address = address;
	}
	
	public void run () {
		
		this.executionEngine.addNeighbor(address, new Vector(0));
		
		Utils.sleep((new Random()).nextInt(Constants.MAX_DISCOVERY_ANSWER_WAITING_TIME));
		Event event = new Event (this.executionEngine.getMyIEEEAddress().asDottedHex(),this.address  , Event.DSC_ANSWER, Event.NOW);
		this.communicationChannelSender.send(event);
		
	}
}
